import threading, Queue, random, thread,time
from math import *
import ga_agent, scripted_object
# This holds all of the agents, and allows for easy human control/selection of them during simulation.  It manages keyboard bindings for control of the current agent.  The agent list may have zero or more agents in it.
# TODO Decide if this should continue to use the real control interface, or if it should be abstracted out to another class (probably a better idea)
class agent_controller(object):
	def __init__(self,agents={},sensors_on=False):
		self.sensors_on=sensors_on
		self.agents=dict(filter(lambda x: self.agent_filter(x),agents.items()))
		for i in self.agents:
			self.cur_agent=i
			break
	def use_agents(self, agents={}):
		self.agents=dict(filter(lambda x: self.agent_filter(x),agents.items()))
		for i in self.agents:
			self.cur_agent=i
			break
	def agent_filter(self, agent):
		'''Any agents without an AI controlling them will be controlled by the human player; this function filters out the AI-controlled agents.'''
		if agent[1].object_type <= 1 or isinstance(agent[1],ga_agent.ga_agent) or isinstance(agent[1],scripted_object.scripted_object):
			return False
		else:
			return True
	def next_agent(self):
		"""Moves to the next valid agent"""
		if len(self.agents) > 0:
			temp_key_list=self.agents.keys()
			new_agent_num=(temp_key_list.index(self.cur_agent)+1) % len(self.agents) # This will give us the index into the temp_key_list of the next agent
			self.cur_agent=temp_key_list[new_agent_num]
			# TODO We will need to notify the agents involved about the change
		
	def new_agent(self,agent_num,agent):
		# TODO Check that the agent doesn't already exist
		self.agents[agent_num]=agent
	def remove_agent(self, agent_num): # removes current agent from agent list
		# TODO We will need to setup a destructor or a function to call when the agent is removed
		remove_agent_num=agent_num
		self.next_agent()
		del self.agents[remove_agent_num]
	# TODO This needs to be corrected so that if an event comes in to delete 
	#an arbitrary agent (from the controller) that it cleans it up elsewhere also
	#def remove_agent_event(self, agent_num): # removes current agent from agent list
	#	self.remove_agent(self.cur_agent)
		
	# Agent Control Settings (to be interfaced with the external controls)
	# All amounts will be 0 or infinite to allow the agent to move at maximum physical speed
	def move_forward(self):
		self.agents[self.cur_agent].actions['move_rel_object'](self.cur_agent,[0,float(1e10000)])
		self.update_sensors()
	def move_backward(self):
		self.agents[self.cur_agent].actions['move_rel_object'](self.cur_agent,[0,float(-1e10000)])
		self.update_sensors()
	def strafe_left(self):
		self.agents[self.cur_agent].actions['move_rel_object'](self.cur_agent,[float(-1e10000),0])
		self.update_sensors()
	def strafe_right(self):
		self.agents[self.cur_agent].actions['move_rel_object'](self.cur_agent,[float(1e10000),0])
		self.update_sensors()
	def move_up(self):
		self.agents[self.cur_agent].actions['move_rel_world'](self.cur_agent,[0,float(-1e10000)])
		self.update_sensors()
	def move_down(self):
		self.agents[self.cur_agent].actions['move_rel_world'](self.cur_agent,[0,float(1e10000)])
		self.update_sensors()
	def move_left(self):
		self.agents[self.cur_agent].actions['move_rel_world'](self.cur_agent,[float(-1e10000),0])
		self.update_sensors()
	def move_right(self):
		self.agents[self.cur_agent].actions['move_rel_world'](self.cur_agent,[float(1e10000),0])
		self.update_sensors()
	def rotate_ccw(self):
		self.agents[self.cur_agent].actions['turn_rel'](self.cur_agent,float(-1e10000))
		self.update_sensors()
	def rotate_cw(self):
		self.agents[self.cur_agent].actions['turn_rel'](self.cur_agent,float(1e10000))
		self.update_sensors()
	def shoot(self):
		self.agents[self.cur_agent].actions['shoot'](self.cur_agent)
		self.update_sensors()
	def update_sensors(self):
		if self.sensors_on:
			for sense in self.agents[self.cur_agent].senses:
				print(str(sense)+': '+str(self.agents[self.cur_agent].senses[sense]()))
